using System;
using System.Collections.Generic;
using SharpWind.Tools;
using SharpWind.Resources.Creators;
using Microsoft.DirectX.Direct3D;

namespace SharpWind.Resources.Managers
{
	public abstract class ResourceManager<T> : IDisposable where T : Resource
	{
		protected class InternalResource<K> where K : Resource
		{
			private K resource = default( K );

			private IResourceCreator<K> creator;

			public K Resource
			{
				get
				{
					return this.resource;
				}
				set
				{
					this.resource = value;
				}
			}

			public IResourceCreator<K> Creator
			{
				get
				{
					return this.creator;
				}
				set
				{
					this.creator = value;
				}
			}
		}

		protected Dictionary<string, InternalResource<T>> resources = new Dictionary<string, InternalResource<T>>();

		internal ResourceManager()
		{
			Device device = Engine.Instance.Renderer.Device;
			device.DeviceLost += new EventHandler( OnDeviceLost );
			device.DeviceReset += new EventHandler( OnDeviceReset );
		}

		public void Clear()
		{
			foreach( InternalResource<T> resource in this.resources.Values )
			{
				SafeDispose.Dispose( resource.Resource );
			}

			this.resources.Clear();
		}

		public void Dispose()
		{
			Clear();
		}

		public void Remove( string name )
		{
			if( name != null && name != string.Empty && this.resources.ContainsKey( name ) )
			{
				this.resources[name].Resource.Dispose();
				this.resources.Remove( name );
			}
		}

		public void Remove( T resource )
		{
			Remove( resource.Name );
		}

		public override string ToString()
		{
			return string.Format( "Count = {0}", Count );
		}

		protected T Create( string name, IResourceCreator<T> creator )
		{
			if( name != null && name != string.Empty )
			{
				if( this.resources.ContainsKey( name ) )
				{
					return this.resources[name].Resource;
				}
				else
				{
					T resource = creator.Create();
					resource.Name = name;

					InternalResource<T> internalResource = new InternalResource<T>();
					internalResource.Resource = resource;
					internalResource.Creator  = creator;

					this.resources.Add( resource.Name, internalResource );

					return resource;
				}
			}
			else
			{
				throw new Exception();
			}
		}

		protected virtual void OnDeviceLost( object sender, EventArgs e )
		{
			foreach( InternalResource<T> resource in this.resources.Values )
			{
				if( resource.Resource != null )
				{
					resource.Resource.Dispose();
					resource.Resource = null;
				}
			}
		}

		protected virtual void OnDeviceReset( object sender, EventArgs e )
		{
			foreach( string key in this.resources.Keys )
			{
				InternalResource<T> resource = this.resources[key];
				
				if( resource.Resource == null )
				{
					resource.Resource      = resource.Creator.Create();
					resource.Resource.Name = key;
				}
			}
		}

		public virtual T this[string name]
		{
			get
			{
				return this.resources[name].Resource;
			}
		}

		public int Count
		{
			get
			{
				return this.resources.Count;
			}
		}
	}
}